' datanode.bmx

Strict

Import brl.Stream
Import brl.StandardIO
Import "tool.bmx"

Type TTreeNode
	Field	node:TGadget,state
End Type

Type TDataNode Extends TTool
	Const	HIDESTATE=0
	Const	CLOSEDSTATE=1
	Const	OPENSTATE=2

	Field	name$,sortname$
	Field	parent:TDataNode
	Field	kids:TList=New TList
	Field	views:TTreeNode[]
' activate program	
	Field	target:TTool
	Field	action$
	Field	argument:Object
	Field	visible=True

' reads name=value pairs and calls SetValue on found names until blank line
' [name] finds name and sets as current scope

	Method Read(stream:TStream)		
		Local	scope:TDataNode
		Local	node:TDataNode
		Local	f$,v$,p,tabs,indent
		Local	stack:TList=New TList

		scope=Self
'		node=Self
		indent=1
		While True
			f$=ReadLine(stream)
			If f$="" Return		

			tabs=0
			While tabs<f.length
				If f[tabs]<>9 Exit
				tabs:+1
			Wend
			f=f[tabs..]
					
			p=f.find("=")
			If p>0
				v$=f[p+1..]
				f$=f[..p]
			EndIf	
			
			While indent>tabs
				scope=TDataNode(stack.RemoveLast())
				indent:-1
			Wend

			If tabs>indent
				stack.addlast scope
				scope=node
				indent=tabs
			EndIf
			
			node=scope.FindNode(f)
			If node 
'				Print "*"+node.name+"="+rhs
				If v$ node.FromString v$
			Else
				Print "creating dummy node "+f
				node=TDataNode.Create(f)
			EndIf
			
			
		Wend
	End Method
	
	Method Write(stream:TStream,indent$)	' blah=blahblah + kids
		Local	kid:TDataNode
		Local	l$,p$
'		If name="" Return		'should throw...
' 11.9.2006 anonymous parents are now removed from tree
		If name
			l$=name$
			p$=ToString()
			If p$ l$=l$+"="+ToString()
'		If kids.count() l$="["+l$+"]"
			WriteLine stream,indent+l$
			indent="~t"+indent
		EndIf
		For kid=EachIn kids
			kid.Write stream,indent
		Next		
	End Method
	
	Method FromString(value$)
	End Method
	
	Method FindNode:TDataNode(withname$)
		Local kid:TDataNode,node:TDataNode
		If name=withname Return Self
		For kid=EachIn kids
			node=kid.FindNode(withname)
			If node Return node
		Next		
	End Method

	Method SortKids( ascending=True )
		Local term:TLink=kids._head
		Repeat
			Local link:TLink=kids._head._succ
			Local sorted=True
			Repeat
				Local succ:TLink=link._succ
				If succ=term Exit
				Local cc=TDataNode(link._value).sortname.Compare( TDataNode(succ._value).sortname )
				If (cc>0 And ascending) Or (cc<0 And Not ascending)
					Local link_pred:TLink=link._pred
					Local succ_succ:TLink=succ._succ
					link_pred._succ=succ
					succ._succ=link
					succ._pred=link_pred
					link._succ=succ_succ
					link._pred=succ
					succ_succ._pred=link
					sorted=False
				Else
					link=succ
				EndIf
			Forever
			If sorted Return
			term=link
		Forever
	End Method

	Method FindArgument:TDataNode(arg:Object)
		Local	n:TDataNode,r:TDataNode,a$
		If arg.Compare(argument)=0 Return Self
		a$=(String(arg)).ToLower()
		If a And a=(String(argument)).toLower() Return Self
		For n=EachIn kids
			r=n.FindArgument(arg)
			If r Return r
		Next
	End Method
	
	Method Dump(indent$="")
		Local	n:TDataNode		
		DebugLog indent+name
		indent:+"~t"
		For n=EachIn kids
			n.Dump indent
		Next
	End Method		
	
	Method IsHidden()
		Local	v:TTreeNode
		If Not parent Return False
		For v=EachIn parent.views
			If v.state=OPENSTATE Return False
		Next
		Return True
	End Method
	
	Method SetAction(tool:TTool,cmd$,arg:Object=Null)
		target=tool
		action=cmd
		argument=arg
	End Method

	Method Hide(v:TTreeNode=Null)	'null means hide in all views
		Local	n:TDataNode
		For n=EachIn kids
			n.hide v
		Next
		If v
			If v.node FreeTreeViewNode v.node;v.node=Null
		Else
			For v=EachIn views
				If v.node FreeTreeViewNode v.node;v.node=Null
			Next
		EndIf
	End Method
	
	Method Detach()
		Hide
		If parent parent.kids.remove Self
		parent=Null
	End Method
		
	Method FreeKids()
		Local	n:TDataNode		
		For n=EachIn kids
			n.free
		Next
	End Method

	Method Free()
		Local	n:TDataNode		
		For n=EachIn kids
			n.free
		Next
		Detach		'Hide()
		views=Null
	End Method

	Method OnMenu()
	End Method
	
	Method Invoke(command$,arg:Object=Null)
		Select command
		Case "ACTIVATE"
			If target Return target.Invoke(action,argument)
		Case "MENU"
			OnMenu
		End Select
	End Method
	
	Method Find:TDataNode(treeviewnode:TGadget,view=0)
		Local	n:TDataNode,r:TDataNode
		Local	v:TTreeNode
		v=getview(view)
		If v And v.node=treeviewnode Return Self
		For n=EachIn kids
			r=n.Find(treeviewnode,view)
			If r Return r
		Next
	End Method
	
	Method SetNode(treeviewnode:TGadget,view)
		Local	v:TTreeNode
		v=getview(view)
		v.node=treeviewnode
		open view
	End Method
	
	Method HighLight(view=-1)
		Local	v:TTreeNode
		If view=-1
			For view=0 To views.length
				HighLight view
			Next
			Return
		EndIf
		v=GetView(view)
		If v.node SelectTreeViewNode v.node
	End Method
	
	Method Open(view=-1)
		Local	v:TTreeNode
		If view=-1
			For view=0 To views.length
				Open view
			Next
			Return
		EndIf
		v=GetView(view)
		If v.state<>OPENSTATE
			v.state=OPENSTATE
			RefreshView view
'			if v.node ExpandTreeViewNode v.node
		EndIf
	End Method
	
	Method Close(view=0)
		Local	v:TTreeNode
		v=GetView(view)
		If v.state<>CLOSEDSTATE
			v.state=CLOSEDSTATE
'			if v.node CollapseTreeViewNode v.node
		EndIf
	End Method
	
	Method GetState(view=0)
		Local	v:TTreeNode
		v=GetView(view)
		Return v.state
	End Method

	Method GetView:TTreeNode(view=0)
		If view>=views.length 
			views=views[..view+1]
			views[view]=New TTreeNode
		EndIf
		If views[view]=Null views[view]=New TTreeNode
		Return views[view]
	End Method
	
	Method GetIndex()
		Local	node:TDataNode
		Local	i		
		If parent
			For node=EachIn parent.kids
				If node=Self Return i
				i:+1
			Next
		EndIf
	End Method
	
	Method Refresh()
		Local	i
		For i=0 To views.length-1
			RefreshView i
		Next
	End Method
	
	Method RefreshView(view=0)
		Local	n:TDataNode,quick
		Local	v:TTreeNode,vv:TTreeNode
		Local	node
		If parent And parent.getstate(view)=CLOSEDSTATE quick=True		
		v=getview(view)
		If v.node And parent
			ModifyTreeViewNode v.node,name
		Else
			If parent And name And visible
				vv=parent.getview(view)
				If vv.node
'					v.node=AddTreeViewNode(name,vv.node)
					v.node=InsertTreeViewNode(GetIndex(),name,vv.node)
					If v.state=HIDESTATE v.state=CLOSEDSTATE					
					If vv.state=OPENSTATE ExpandTreeViewNode vv.node
					quick=False
				EndIf
			EndIf
		EndIf
		If quick Return
		If Not kids Return
		For n=EachIn kids
			n.RefreshView view
		Next
	End Method

	Method NodeAfter:TDataNode(node:TDataNode)
		Local	link:TLink
		If node link=kids.FindLink(node)
		If link link=link.NextLink()
		If link Return TDataNode(link.Value())	
	End Method

	Method Sync(snap:TDataNode)
		Local	snapkid:TDataNode
		Local	currentkid:TDataNode
		Local	kid:TDataNode
		Local	t:TDataNode
		Local	link:TLink

		If snap.name<>name Return
		If kids.Count() currentkid=TDataNode(kids.First())
		For snapkid=EachIn snap.kids
' if same name in list 
			kid=currentkid
			While kid
				If kid.name=snapkid.name Exit
				kid=NodeAfter(kid)
			Wend
' then remove entries in front			
			If kid
				While currentkid<>kid
					t=currentkid
					currentkid=NodeAfter(currentkid)			
					t.free()
				Wend
			EndIf
' if same name sync else insert 
			If currentkid And currentkid.name=snapkid.name	'merge values if same name 
				currentkid.Sync snapkid
				currentkid=NodeAfter(currentkid)
			Else
				snapkid.detach
				If currentkid
					link=kids.FindLink(currentkid)
					kids.InsertBeforeLink snapkid,link
				Else
					kids.AddLast snapkid
				EndIf
				snapkid.parent=Self
			EndIf
		Next
' remove any entries at end
		While currentkid
			t=currentkid
			currentkid=NodeAfter(currentkid)			
			t.free()
		Wend
		Refresh()
	End Method

	Method SetName(n$)
		name=n
	End Method
		
	Method AddNode:TDataNode(name$)
		Local	v:TDataNode
		v=New TDataNode
		v.setname name
		Append v
		Return v
	End Method
	
	Method Append(kid:TDataNode)
		kid.Detach
		kid.parent=Self
		kids.AddLast kid
	End Method	
	
	Method AddFirst(kid:TDataNode)
		kid.Detach
		kid.parent=Self
		kids.AddFirst kid
	End Method	

	Function Create:TDataNode(name$)
		Local	node:TDataNode
		node=New TDataNode
		node.setname name
		Return node
	End Function		
End Type
